#include <mTask.h>
#include <rbtree.h>

static mTaskMutex_t     mStaticTimerMutex;
static mTaskMutex_t     mDynamicTimerMutex;
static struct rb_root   mStaticTimerList;
static struct rb_root   mDynamicTimerList;
static struct mList     mFreeList;
static mTimer_t         *pStaticRunTimer = NULL;
static mTimer_t         *pDynamicRunTimer = NULL;
static int RbTreeInsert(struct rb_root *root, mTimer_t *timer);
static int RbDetechTimer(struct rb_root *root, mTimer_t *timer);
static void UpdateStaticRunTimer(void);
static void UpdateDynamicRunTimer(void);
static void InitTimerMutex(void);

mTimer_t* mTaskCreatDynamicTimer(void)
{
    mTimer_t *timer;
    InitTimerMutex();
    mTaskMutexLock(&mDynamicTimerMutex);
    timer = (mTimer_t*)mListDetachFront(&mFreeList);
    mTaskMutexUnlock(&mDynamicTimerMutex);
    if(timer==NULL)
    {
        timer = (mTimer_t*)malloc(sizeof(timer));
    }
    return timer;
}
int mTaskInitTimer(mTimer_t *timer, mTimerCb_t cb, void *arg1, void *arg2)
{
    InitTimerMutex();
    timer->cb = cb;
    timer->arg1 = arg1;
    timer->arg2 = arg2;
    return mTASK_SUCCESS;
}
int mTaskStartStaticTimer(mTimer_t *timer, uint64_t expire, int reload)
{
    mTaskMutexLock(&mStaticTimerMutex);
    timer->expire = expire + mTaskGetMicroSecond();
    timer->runtime = (reload)?expire:0;
    RbTreeInsert(&mStaticTimerList,timer);
    UpdateStaticRunTimer();
    mTaskMutexUnlock(&mStaticTimerMutex);
}
int mTaskStartDynamicTimer(mTimer_t *timer, uint64_t expire, int reload)
{
    mTaskMutexLock(&mDynamicTimerMutex);
    timer->expire = expire + mTaskGetMicroSecond();
    timer->runtime = (reload)?expire:0;
    RbTreeInsert(&mDynamicTimerList,timer);
    UpdateDynamicRunTimer();
    mTaskMutexUnlock(&mDynamicTimerMutex);
}
int mTaskStopStaticTimer(mTimer_t *timer)
{
    mTaskMutexLock(&mStaticTimerMutex);
    RbDetechTimer(&mStaticTimerList,timer);
    UpdateStaticRunTimer();
    mTaskMutexUnlock(&mStaticTimerMutex);
    return mTASK_SUCCESS;
}
int mTaskStopDynamicTimer(mTimer_t *timer)
{
    mTaskMutexLock(&mDynamicTimerMutex);
    RbDetechTimer(&mDynamicTimerList,timer);
    UpdateDynamicRunTimer();
    mListInsertFront(&mFreeList,(mListNode_t*)timer);
    mTaskMutexUnlock(&mDynamicTimerMutex);
    return mTASK_SUCCESS;
}
int mTaskStaticTimerPoll(void)
{
    mTimer_t *ptimer;
    uint64_t temp;
    while(pStaticRunTimer!=NULL)
    {
        temp = mTaskGetMicroSecond();
        if(pStaticRunTimer->expire<=temp)
        {
            mTaskMutexLock(&mStaticTimerMutex);
            ptimer = pStaticRunTimer;
            RbDetechTimer(&mStaticTimerList,ptimer);
            mTaskMutexUnlock(&mStaticTimerMutex);
            if(ptimer->cb!=NULL)
            {
                ptimer->cb(ptimer->arg1,ptimer->arg2);
            }
            if(ptimer->runtime)
            {
                mTaskMutexLock(&mStaticTimerMutex);
                ptimer->expire += ptimer->runtime;
                RbTreeInsert(&mStaticTimerList,ptimer);
                UpdateDynamicRunTimer();
                mTaskMutexUnlock(&mStaticTimerMutex);
            }
            else
            {
                mTaskMutexLock(&mStaticTimerMutex);
                mListInsertFront(&mFreeList,(mListNode_t*)ptimer);
                UpdateDynamicRunTimer();
                mTaskMutexUnlock(&mStaticTimerMutex);
                continue;
            }
        }
        else
        {
            break;
        }
    }
    if(pDynamicRunTimer!=NULL)
        return mTASK_SUCCESS;
    else
        return mTASK_FAILED;
}
int mTaskDynamicTimerPoll(void)
{
    mTimer_t *ptimer;
    uint64_t temp;
    while(pDynamicRunTimer!=NULL)
    {
        temp = mTaskGetMicroSecond();
        if(pDynamicRunTimer->expire<=temp)
        {
            mTaskMutexLock(&mDynamicTimerMutex);
            ptimer = pDynamicRunTimer;
            RbDetechTimer(&mDynamicTimerList,ptimer);
            mTaskMutexUnlock(&mDynamicTimerMutex);
            if(ptimer->cb!=NULL)
            {
                ptimer->cb(ptimer->arg1,ptimer->arg2);
            }
            if(ptimer->runtime)
            {
                mTaskMutexLock(&mDynamicTimerMutex);
                ptimer->expire += ptimer->runtime;
                RbTreeInsert(&mDynamicTimerList,ptimer);
                UpdateDynamicRunTimer();
                mTaskMutexUnlock(&mDynamicTimerMutex);
            }
            else
            {
                mTaskMutexLock(&mDynamicTimerMutex);
                mListInsertFront(&mFreeList,(mListNode_t*)ptimer);
                UpdateDynamicRunTimer();
                mTaskMutexUnlock(&mDynamicTimerMutex);
                continue;
            }
        }
        else
        {
            break;
        }
    }
    if(pDynamicRunTimer!=NULL)
        return mTASK_SUCCESS;
    else
        return mTASK_FAILED;
}

static void UpdateStaticRunTimer(void)
{
    pStaticRunTimer = (mTimer_t*)rb_first(&mStaticTimerList);mTaskMutexLock(&mDynamicTimerMutex);
}
static void UpdateDynamicRunTimer(void)
{
    pDynamicRunTimer = (mTimer_t*)rb_first(&mDynamicTimerList);
}
static int RbTreeInsert(struct rb_root *root, mTimer_t *timer)
{
  	struct rb_node **node = &(root->rb_node), *parent = NULL;
  	/* Figure out where to put node node */
  	while (*node) {
  		mTimer_t *this = container_of(*node, mTimer_t , rbNode);
		parent = *node;
        if(timer->expire >= this->expire)
        {
            node = &((*node)->rb_right);
        }
        else// if(data->key < this->key)
        {
            node = &((*node)->rb_left);
        }
        //else{
            //return 0;
        //}
  	}
  	/* Add new node and rebalance tree. */
  	rb_link_node(&timer->rbNode, parent, node);
  	rb_insert_color(&timer->rbNode, root);
	return mTASK_SUCCESS;
}
static int RbDetechTimer(struct rb_root *root, mTimer_t *timer)
{
	if (timer != NULL) {
        rb_erase(&timer->rbNode, root);
        return mTASK_SUCCESS;
	}
    return mTASK_FAILED;
}
static void InitTimerMutex(void)
{
    static char initflag = 0x00;
    if(initflag==0x00)
    {
        mTaskMutexInit(&mStaticTimerMutex);
        mTaskMutexInit(&mDynamicTimerMutex);
        initflag = 0x01;
    }
}

